﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Speech.Recognition;
using System.IO;
using System.Diagnostics;

namespace Handsfree
{
    public struct Command
    {
        public String speech, action, type;
        public Command(String type, String speech, String action)
        {
            this.action = action;
            this.speech = speech;
            this.type = type;
        }
    }

    public class SpeechRecognition
    {

        #region Member variables
        //Set of possible Gramars
        enum Grammars
        {
            Application,
            Mouse,
            Keyboard,
            KeySet,
            Run,
            Special,
        };
        private static SpeechRecognitionEngine recognitionEngine;
        private static List<Command> allCommands;
        private MouseOperations m;
        private KeyboardOperation k;
        private UDPReceive u;
        private static bool SpeechRecognitionOn;
        #endregion


        #region Constructor Methods
        public SpeechRecognition()
        {
            m = new MouseOperations();
            k = new KeyboardOperation();
            u = new UDPReceive(false);
            allCommands = new List<Command>();
            getAllCommands();
            recognitionEngine = new SpeechRecognitionEngine();
            recognitionEngine.SetInputToDefaultAudioDevice();
            recognitionEngine.SpeechDetected += new EventHandler<SpeechDetectedEventArgs>(recognitionEngine_SpeechDetected);
            recognitionEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(recognitionEngine_SpeechRecognized);
            loadGrammars();
        }
        public SpeechRecognition(bool a)
        {
            //Overload Constructor to ensure SpeechEngine is not initiated
            //which leads to duplicate result
            //SpeechRecognition class is used in Add_New Class
        }

        public void ReloadGrammar()
        {
            try
            {
                StopEngine();
            }
            catch (Exception)
            {
                Console.WriteLine("Engine already Stopped! before restarting");
            }
            UnloadGrammars();
            getAllCommands();
            loadGrammars();
            StartEngine();
        }
        //Sart Recognition Engine
        public void StartEngine()
        {
            recognitionEngine.RecognizeAsync(RecognizeMode.Multiple);
            SpeechRecognitionOn = true;
        }
        //Stop Recognition Engine
        public void StopEngine()
        {
            recognitionEngine.RecognizeAsyncStop();
            SpeechRecognitionOn = false;
        }
        //Returns the list of current Commands
        public List<Command> returnCommands()
        {
            return allCommands;
        }

        //Reads the list of command from text file
        public void getAllCommands()
        {
            try
            {
                // Create an instance of StreamReader to read from a file.
                // The using statement also closes the StreamReader.
                Preferences p = new Preferences();
                using (StreamReader sr = new StreamReader(p.getPath()))
                {
                    String line;
                    // Read and display lines from the file until the end of
                    // the file is reached.
                    while ((line = sr.ReadLine()) != null)
                    {
                        string[] words = line.Split('/');
                        Command c = new Command(words[0], words[1], words[2]);
                        allCommands.Add(c);
                    }
                }
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                MessageBox.Show("The commands file could not be read.\n" + e.ToString());
            }
        }
        //Returns the culture of SpeechRecognitionEngine
        private static System.Globalization.CultureInfo GetCulture()
        {
            return recognitionEngine.RecognizerInfo.Culture;
        }
        #endregion

        #region Event Handlers and Methods for Speech Recognition

        private void recognitionEngine_SpeechDetected(object sender, SpeechDetectedEventArgs detection)
        {
            //Console.WriteLine("Speech Detected\n");
        }

        private void recognitionEngine_SpeechRecognized(object sender, RecognitionEventArgs r)
        {
            Preferences pref = new Preferences();
            float thres = pref.getRobustnessValue();//Computes value of thres from Preferences 
            /*
             * If user has entered Stop Listening Mode, then dont listen
             * except for Special Commands one of which can be Start Listening
             * 
             * Also dont process the text if it is less than the threshold
             * set in Preferences
             */
            if ((!SpeechRecognitionOn && r.Result.Grammar.Name!="Special") || (int)r.Result.Confidence<thres)
                return;

            //Process The Input
            RecognitionResult result = r.Result;
            String g = result.Grammar.Name;

            //Preprocess the text to exract the command only and store it in s
            String s = result.Text.ToString();
            //Remove first Word from Application, Keyboard and Run
            if (g == "Application" || g == "Keyboard" || g == "Run")
            {
                s = s.Substring(s.IndexOf(' ') + 1);
            }
            //If command is in grammar Application, Keyset or Run
            //get the appropiate target from allCommands
            if (g == "Application" || g == "Run" || g == "KeySet" || g == "Keyboard")
                s = getActionfromSpeech(s);
            if (s == "")
            {
                MessageBox.Show("The command could not be found.");
                return;
            }
            //Call the appropiate Method
            switch (g)
            {
                case "Application":
                    RecognisedApplication(s);
                    break;
                case "Mouse":
                    RecognisedMouse(s);
                    break;
                case "Keyboard":
                    RecognisedKeyboard(s);
                    break;
                case "KeySet":
                    RecognisedKeySet(s);
                    break;
                case "Run":
                    RecognisedRun(s);
                    break;
                case "Special":
                    RecognisedSpecial(s);
                    break;
            }
        }
        public bool RecognisedApplication(String s)
        {
            string str = @s;
            try
            {
                Process process = new Process();
                process.StartInfo.FileName = str;
                if(!process.Start())
                    return false;
            }
            catch
            {
                MessageBox.Show("Invalid Application");
                return false;
            }
            return true;
        }
        private void RecognisedMouse(String s)
        {
            switch (s)
            {
                case "Click":
                    m.Click(1);
                    break;
                case "Left Click":
                    m.Click(1);
                    break;
                case "Double Click":
                    m.Click(2);
                    break;
                case "Right Click":
                    m.RightClick(1);
                    break;
                case "Middle Click":
                    m.MiddleClick(1);
                    break;
                case "Move Up":
                    m.MoveMouse(0, -10);
                    break;
                case "Move Down":
                    m.MoveMouse(0, 10);
                    break;
                case "Move Right":
                    m.MoveMouse(10, 0);
                    break;
                case "Move Left":
                    m.MoveMouse(-10, 0);
                    break;
            }
        }
        private void RecognisedKeyboard(String s)
        {
            RecognisedKeySet(s);
        }
        private void RecognisedKeySet(String s)
        {
            string[] words = s.Split('+');
            for (int i = 0; i < words.Length; i++)
                k.KeyPress(words[i]);
            for (int i = words.Length - 1; i >= 0; i--)
                k.KeyRelease(words[i]);
        }
        public bool RecognisedRun(String s)
        {
            try
            {
                int a=(s.Contains(' '))?s.IndexOf(' '):s.Length;
                string filename = s.Substring(0,a);
                string workDir = System.IO.Path.GetDirectoryName(filename);
                ProcessStartInfo startInfo = new ProcessStartInfo(filename);
                startInfo.UseShellExecute = true;
                startInfo.WorkingDirectory = workDir;
                if (a!=s.Length)
                {
                    string args = s.Substring(s.IndexOf(' ') + 1);
                    startInfo.Arguments = args;
                }
                if(Process.Start(startInfo)==null)
                    return false ;
            }
            catch (Exception /*e*/)
            {
                MessageBox.Show("Illegal Service!");
                return false;
            }
            return true;
        }
        private void RecognisedSpecial(String s)
        {
            switch (s)
            {
                case "Stop Listening":
                    SpeechRecognitionOn = false;
                    break;
                case "Start Listening":
                    SpeechRecognitionOn = true; ;
                    break;
                case "Stop Tracking":
                    u.StopEngine();
                    break;
                case "Start Tracking":
                    u.StartEngine();
                    break;
                case "Restart Listening":
                    ReloadGrammar();
                    break;
                case "Restart Tracking":
                    u.init();
                    break;
            }
        }

        private String getActionfromSpeech(String s)
        {
            foreach (Command c in allCommands)
            {
                if (c.speech.ToLower() == s.ToLower())
                    return c.action;
            }
            return "";
        }
        #endregion


        #region GrammarBuilders
        /*
         * The Workflow for adding Grammar to SpeechRecognitionEngine is as follows:
         *      1. Call to loadGrammars() in constructor loads all commands
         *      2. loadGrammars calls loadGrammar(GrammarName) for each Grammar
         *      3. loadGrammars queries apt function to get the possible Choices (XML reader)
         */
        public static void loadGrammars()
        {
            foreach (Grammars g in Enum.GetValues(typeof(Grammars)))
                loadGrammar(g);
        }
        public static void UnloadGrammars()
        {
            recognitionEngine.UnloadAllGrammars();
        }

        private static void loadGrammar(Grammars commandType)
        {
            Grammar gr;
            GrammarBuilder commandGrammar = new GrammarBuilder();
            commandGrammar.Culture = GetCulture() ;
            switch (commandType)
            {
                case Grammars.Mouse:
                    commandGrammar.Append(new Choices("Click", "Double Click", 
                        "Left Click", "Right Click", "Middle Click", "Move Up", 
                        "Move Down", "Move Right", "Move Left"));
                    gr = new Grammar(commandGrammar);
                    gr.Name = commandType.ToString();
                    break;
                case Grammars.Special:
                    commandGrammar.Append(new Choices("Start Listening", "Stop Listening", 
                        "Restart Listening", "Start Tracking", "Stop Tracking", "Restart Tracking"));
                    gr = new Grammar(commandGrammar);
                    gr.Name = commandType.ToString();
                    break;
                default:
                    if (getCommands(commandType).Count == 0)
                        return;
                    gr = CommandsGrammarBuilder(GetCulture(), commandType);
                    break;
            }
            recognitionEngine.LoadGrammar(gr);
                    
        }

        /* CommandsGrammarBuilder
         * Builds the Grammar adding necessary Prefixes like
         * "Open" before application name
         * "Press" before Keyboards
         */
        static private Grammar CommandsGrammarBuilder(System.Globalization.CultureInfo culture, Grammars commandType)
        {
            //Build the Grammar
            GrammarBuilder commandGrammar = new GrammarBuilder();
            commandGrammar.Culture = culture;

            //Adds the necessary prefix
            switch (commandType)
            {
                case Grammars.Application:
                    commandGrammar.Append(new Choices("Open"));
                    break;
                case Grammars.KeySet:
                    break;
                case Grammars.Run:
                    commandGrammar.Append(new Choices("Run"));
                    break;
                case Grammars.Keyboard:
                    commandGrammar.Append(new Choices("Press"));
                    break;
            }

            //Adds the Speech Command
            List<String> arr = getCommands(commandType);
            Choices choices = new Choices();
            foreach (String c in arr)
            {
                choices.Add(c);
            }
            commandGrammar.Append(new SemanticResultKey("command", choices));
            Grammar g = new Grammar(commandGrammar);
            g.Name = commandType.ToString();
            return g;
        }

        private static List<String> getCommands(Grammars g)
        {
            List<String> res = new List<string>();
            foreach (Command c in allCommands)
            {
                if (c.type == g.ToString())
                {
                    res.Add(c.speech.ToString());
                }
            }
            return res;
        }
        #endregion GrammarBuilders

    }
}